package zilvic.resources;

/**
 *
 * @author Duncan
 */
public class ByteMagic {
    public static byte[] intToBytes(int value) {
        return new byte[] {
                (byte) (value >>> 24),
                (byte) (value >>> 16),
                (byte) (value >>> 8),
                (byte) (value)};
    }

    //XXX Test to see that bytesToInt is working correctly.
    public static int bytesToInt(byte[] bytes) {
        int result = 0;
        for (int i = 0; i < 4; i++) {
            result = (result << 8) - Byte.MIN_VALUE + (int) bytes[i];
        }
        return result;
    }

    public static byte[] longToBytes(long value) {
        return new byte[] {
            (byte) (value >>> 56),
            (byte) (value >>> 48),
            (byte) (value >>> 40),
            (byte) (value >>> 32),
            (byte) (value >>> 24),
            (byte) (value >>> 16),
            (byte) (value >>>  8),
            (byte) (value)};
    }

    //XXX Test to see that bytesToLong is working correctly.
    public static long bytesToLong(byte[] bytes) {
        long result = 0;
        for (int i = 0; i < 8; i++) {
            result = (result << 8) - Byte.MIN_VALUE + (long) bytes[i];
        }
        return result;
    }

    public static byte booleanToByte(boolean isTrue) {
        if (isTrue) return 1;
        else return 0;
    }

    public static boolean byteToBoolean(byte value) {
        return (value != 0);
    }

    public static byte[] booleanToBytes(boolean isTrue) {
        if (isTrue) return new byte[]{1};
        else return new byte[]{0};
    }

    public static boolean bytesToBoolean(byte[] bytes) {
        return byteToBoolean(bytes[0]);
    }

    //XXX test to see that compressBytes is working correctly.
    public static byte[] compressBytes(byte[][] bytes) {
        int length = bytes.length * 4 + 4;
        for (int i = 0; i < bytes.length; i++) {
            length += bytes[i].length;
        }
        byte[] compressed = new byte[length];
        byte[] l = intToBytes(bytes.length);
        int i = 0;
        while (i < 4) {
            compressed[i] = l[i++];
        }
        for (int n = 0; i < length; n++) {
            byte[] byteInt = intToBytes(bytes[n].length);
            compressed[i++] = byteInt[0];
            compressed[i++] = byteInt[1];
            compressed[i++] = byteInt[2];
            compressed[i++] = byteInt[3];
            for (int j = 0; j < bytes[n].length;) {
                compressed[i++] = bytes[n][j++];
            }
        }
        return compressed;
    }

    //XXX Test to see if uncompressBytes is working correctly.
    public static byte[][] uncompressBytes(byte[] bytes) {
        int i = 0, n = -1;
        byte[][] uncompressed = new byte[bytesToInt(new byte[]
                    {bytes[i++], bytes[i++], bytes[i++], bytes[i++]})][];
        while (i < bytes.length) {
            int length = bytesToInt(new byte[] {
                bytes[i++], bytes[i++], bytes[i++], bytes[i++]});
            uncompressed[n++] = new byte[length];
            for (int j = 0; j < length; j++) {
                uncompressed[n][j] = bytes[i++];
            }
        }
        return uncompressed;
    }

    public static byte[] doubleToBytes(double value) {
        return longToBytes(Double.doubleToLongBits(value));
    }

    public static double bytesToDouble(byte[] bytes) {
        return Double.longBitsToDouble(bytesToLong(bytes));
    }

    //XXX parsablesToBytes needs testing. A lot.
    public static byte[] parsablesToBytes(Parsable[] parsables) {
        byte[][] bytes = new byte[parsables.length * 2][];
        for (int i = 0; i < parsables.length; i += 2) {
            bytes[i]     = stringToBytes(parsables[(int) (i / 2)].getClass().getName());
            bytes[i + 1] = parsables[i].toBytes();
        }
        return ByteMagic.compressBytes(bytes);
    }

    public static Parsable[] bytesToParsables(byte[] b) {
        byte[][] bytes = uncompressBytes(b);
        Parsable[] parsables = new Parsable[bytes.length / 2];
        int i = 0;
        while (i < bytes.length) {
            try {
                parsables[(int) (i / 2)] = (Parsable) ClassLoader.getSystemClassLoader().loadClass(bytesToString(bytes[i])).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            parsables[(int) (i++ / 2)].fromBytes(bytes[i++]);
        }
        return parsables;
    }

    public static byte[] stringToBytes(String str) {
        return str.getBytes();
    }

    public static String bytesToString(byte[] bytes) {
        return new String(bytes);
    }
}